#ifndef _WX_DCBUFFER_H_
#define _WX_DCBUFFER_H_

#include "wx/dcmemory.h"
#include "wx/dcclient.h"
#include "wx/window.h"

#if defined(__WXMAC__) || defined(__WXGTK20__) || defined(__WXDFB__)
#define wxALWAYS_NATIVE_DOUBLE_BUFFER       1
#else
#define wxALWAYS_NATIVE_DOUBLE_BUFFER       0
#endif

#define wxBUFFER_VIRTUAL_AREA       0x01
#define wxBUFFER_CLIENT_AREA        0x02

class wxBufferedDC : public wxMemoryDC {
  public:
    wxBufferedDC()
      : m_dc( NULL ),
        m_buffer( NULL ),
        m_style( 0 ) {
    }
    wxBufferedDC( wxDC *dc,
                  wxBitmap& buffer = wxNullBitmap,
                  int style = wxBUFFER_CLIENT_AREA )
      : m_dc( NULL ), m_buffer( NULL ) {
      Init( dc, buffer, style );
    }
    wxBufferedDC( wxDC *dc, const wxSize& area, int style = wxBUFFER_CLIENT_AREA )
      : m_dc( NULL ), m_buffer( NULL ) {
      Init( dc, area, style );
    }
    virtual ~wxBufferedDC() {
      if( m_dc ) {
        UnMask();
      }
    }
    void Init( wxDC *dc,wxBitmap& buffer = wxNullBitmap,int style = wxBUFFER_CLIENT_AREA ) {
      InitCommon( dc, style );
      m_buffer = &buffer;
      UseBuffer();
    }

    void Init( wxDC *dc, const wxSize &area, int style = wxBUFFER_CLIENT_AREA ) {
      InitCommon( dc, style );
      UseBuffer( area.x, area.y );
    }
    void UnMask() {
      wxCHECK_RET( m_dc, wxT( "no underlying wxDC?" ) );
      wxASSERT_MSG( m_buffer && m_buffer->IsOk(), wxT( "invalid backing store" ) );
      wxCoord x = 0,
              y = 0;
      if( m_style & wxBUFFER_CLIENT_AREA ) {
        GetDeviceOrigin( &x, &y );
      }
      m_dc->Blit( 0, 0, m_buffer->GetWidth(), m_buffer->GetHeight(),
                  this, -x, -y );
      m_dc = NULL;
    }
    void SetStyle( int style ) { m_style = style; }
    int GetStyle() const { return m_style; }

  private:
    void InitCommon( wxDC *dc, int style ) {
      wxASSERT_MSG( !m_dc, wxT( "wxBufferedDC already initialised" ) );
      m_dc = dc;
      m_style = style;
      if( dc && dc->IsOk() ) {
        SetLayoutDirection( dc->GetLayoutDirection() );
      }
    }
    void UseBuffer( wxCoord w = -1, wxCoord h = -1 );
    wxDC *m_dc;
    wxBitmap *m_buffer;
    int m_style;
    DECLARE_DYNAMIC_CLASS( wxBufferedDC )
    DECLARE_NO_COPY_CLASS( wxBufferedDC )
};

class wxBufferedPaintDC : public wxBufferedDC {
  public:
    wxBufferedPaintDC( wxWindow *window, wxBitmap& buffer, int style = wxBUFFER_CLIENT_AREA )
      : m_paintdc( window ) {
      if( style & wxBUFFER_VIRTUAL_AREA ) {
        window->PrepareDC( m_paintdc );
      }
      if( buffer.IsOk() ) {
        Init( &m_paintdc, buffer, style );
      } else{ Init( &m_paintdc, GetBufferedSize( window, style ), style ); }
    }
    wxBufferedPaintDC( wxWindow *window, int style = wxBUFFER_CLIENT_AREA )
      : m_paintdc( window ) {
      if( style & wxBUFFER_VIRTUAL_AREA ) {
        window->PrepareDC( m_paintdc );
      }
      Init( &m_paintdc, GetBufferedSize( window, style ), style );
    }
    virtual ~wxBufferedPaintDC() {
      UnMask();
    }
  protected:
    static wxSize GetBufferedSize( wxWindow *window, int style ) {
      return style & wxBUFFER_VIRTUAL_AREA ? window->GetVirtualSize()
             : window->GetClientSize();
    }

  private:
    wxPaintDC m_paintdc;

    DECLARE_ABSTRACT_CLASS( wxBufferedPaintDC )
    DECLARE_NO_COPY_CLASS( wxBufferedPaintDC )
};

#if wxALWAYS_NATIVE_DOUBLE_BUFFER
#define wxAutoBufferedPaintDCBase           wxPaintDC
#else
#define wxAutoBufferedPaintDCBase           wxBufferedPaintDC
#endif

typedef wxAutoBufferedPaintDCBase wxAutoBufferedPaintDC;

inline wxDC* wxAutoBufferedPaintDCFactory( wxWindow* window ) {
  if( window->IsDoubleBuffered() ) {
    return new wxPaintDC( window );
  } else {
    return new wxBufferedPaintDC( window );
  }
}

#endif
